import random
import string


# ==============================================================================
# 1. 地址类 (Address)
# ==============================================================================
class Address:
    """
    地址类，用于存储用户的地址信息。
    """

    def __init__(self, country: str, province: str, street: str, house_number: str):
        self.country = country
        self.province = province
        self.street = street
        self.house_number = house_number

    def __str__(self):
        """
        返回格式化的地址字符串，方便打印。
        """
        return f"{self.country}{self.province}{self.street}{self.house_number}"


# ==============================================================================
# 2. 用户类 (User)
# ==============================================================================
class User:
    """
    用户类，用于存储单个用户的账户信息。
    """

    def __init__(self, account_number: str, account_type: int, name: str, password: int, address: Address,
                 opening_bank: str, balance: int = 0):
        self.account_number = account_number
        self.account_type = account_type
        self.name = name
        self.password = password
        self.address = address
        self.balance = balance
        self.opening_bank = opening_bank

    def __str__(self):
        """
        返回格式化的用户信息字符串，用于查询功能。
        """
        return (f"--- 账户信息 ---\n"
                f"当前账号：{self.account_number}\n"
                f"账户类型：{self.account_type}\n"
                f"户主姓名：{self.name}\n"
                f"密码：******\n"  # 出于安全考虑，不直接显示密码
                f"余额：{self.balance} 元\n"
                f"用户居住地址：{self.address}\n"
                f"开户行：{self.opening_bank}\n"
                f"----------------")


# ==============================================================================
# 3. 银行类 (Bank)
# ==============================================================================
class Bank:
    """
    银行类，负责处理所有核心银行业务逻辑。
    """

    def __init__(self, name: str, capacity: int = 100):
        self.name = name
        self.capacity = capacity
        # 用户库：使用字典存储，键为账号，值为User对象，方便快速查找
        self.users = {}

    def _generate_account_number(self) -> str:
        """
        内部方法：生成一个唯一的8位数字账号。
        """
        while True:
            # 生成8位数字组成的字符串
            account_num = ''.join(random.choices(string.digits, k=8))
            # 确保生成的账号是唯一的
            if account_num not in self.users:
                return account_num

    def open_account(self, name: str, password: int, address: Address, account_type: int,
                     initial_deposit: int) -> tuple:
        """
        开户业务
        :param name: 姓名
        :param password: 密码
        :param address: 地址对象
        :param account_type: 账户类型
        :param initial_deposit: 初始存款
        :return: 元组 (状态码, 新账号或None)
                 状态码 -> 1: 成功, 2: 用户已存在(按姓名判断), 3: 用户库已满
        """
        # 3. 检查用户库是否已满
        if len(self.users) >= self.capacity:
            return (3, None)

        # 2. 检查同名用户是否已存在 (业务逻辑优化，实际银行可能允许多个同名用户)
        for user in self.users.values():
            if user.name == name:
                return (2, None)

        # 1. 创建新用户
        new_account_number = self._generate_account_number()
        new_user = User(
            account_number=new_account_number,
            account_type=account_type,
            name=name,
            password=password,
            address=address,
            opening_bank=self.name,
            balance=initial_deposit
        )
        self.users[new_account_number] = new_user
        return (1, new_account_number)

    def deposit(self, account_number: str, amount: int) -> bool:
        """
        存款业务
        :param account_number: 存款账号
        :param amount: 存款金额
        :return: 布尔值 (True: 成功, False: 账号不存在)
        """
        user = self.users.get(account_number)
        if not user:
            return False

        user.balance += amount
        return True

    def withdraw(self, account_number: str, password: int, amount: int) -> int:
        """
        取款业务
        :param account_number: 取款账号
        :param password: 密码
        :param amount: 取款金额
        :return: 整型值 (0: 正常, 1: 账号不存在, 2: 密码不对, 3: 钱不够)
        """
        user = self.users.get(account_number)
        # 1. 账号不存在
        if not user:
            return 1

        # 2. 密码不对
        if user.password != password:
            return 2

        # 3. 钱不够
        if user.balance < amount:
            return 3

        # 0. 正常取款
        user.balance -= amount
        return 0

    def transfer(self, from_account: str, to_account: str, password: int, amount: int) -> int:
        """
        转账业务
        :param from_account: 转出账号
        :param to_account: 转入账号
        :param password: 转出账号密码
        :param amount: 转账金额
        :return: 整型值 (0: 正常, 1: 账号不对, 2: 密码不对, 3: 钱不够)
        """
        from_user = self.users.get(from_account)
        to_user = self.users.get(to_account)

        # 1. 账号不对 (转出或转入账号不存在)
        if not from_user or not to_user:
            return 1

        # 2. 密码不对
        if from_user.password != password:
            return 2

        # 3. 钱不够
        if from_user.balance < amount:
            return 3

        # 0. 正常转账
        from_user.balance -= amount
        to_user.balance += amount
        return 0

    def query_account(self, account_number: str, password: int):
        """
        查询账户功能 (此功能直接打印，无返回值)
        :param account_number: 查询账号
        :param password: 密码
        """
        user = self.users.get(account_number)
        if not user:
            print("查询失败：该用户不存在。")
            return

        if user.password != password:
            print("查询失败：密码错误。")
            return

        # 账号密码正确，打印用户信息
        print(user)


# ==============================================================================
# 4. ATM界面类 (ATM)
# ==============================================================================
class ATM:
    """
    ATM机界面类，负责与用户交互。
    """

    def __init__(self, bank: Bank):
        self.bank = bank

    def _display_menu(self):
        """显示主菜单"""
        print("\n" + "=" * 30)
        print("   欢迎使用中国农业银行ATM系统")
        print("      " + self.bank.name)
        print("=" * 30)
        print("  1. 开户")
        print("  2. 存钱")
        print("  3. 取钱")
        print("  4. 转账")
        print("  5. 查询")
        print("  6. 退出")
        print("-" * 30)

    def _get_input(self, prompt: str, type_cast=str, validation=None, error_msg="无效输入"):
        """通用输入处理函数，带类型转换和验证"""
        while True:
            try:
                value = type_cast(input(prompt))
                if validation is None or validation(value):
                    return value
                else:
                    print(error_msg)
            except ValueError:
                print(f"输入格式错误，请输入{type_cast.__name__}类型。")

    def _handle_open_account(self):
        print("\n[办理开户业务]")
        name = self._get_input("请输入您的姓名: ")
        password = self._get_input(
            "请输入您的6位数字密码: ",
            int,
            lambda p: len(str(p)) == 6,
            "密码必须是6位数字！"
        )
        # 收集地址信息
        country = self._get_input("请输入国家: ")
        province = self._get_input("请输入省份: ")
        street = self._get_input("请输入街道: ")
        house_number = self._get_input("请输入门牌号: ")
        address = Address(country, province, street, house_number)

        account_type = self._get_input("请输入账户类型(例如: 1-储蓄卡, 2-信用卡): ", int)
        initial_deposit = self._get_input("请输入初始存款金额: ", int, lambda d: d >= 0, "存款金额不能为负！")

        status, new_account = self.bank.open_account(name, password, address, account_type, initial_deposit)

        if status == 1:
            print(f"\n开户成功！您的新账号是: {new_account}")
        elif status == 2:
            print("\n开户失败：该姓名已被注册。")
        elif status == 3:
            print("\n开户失败：银行用户库已满，请联系管理员。")

    def _handle_deposit(self):
        print("\n[办理存款业务]")
        account = self._get_input("请输入您的账号: ")
        amount = self._get_input("请输入存款金额: ", int, lambda a: a > 0, "存款金额必须大于0！")

        if self.bank.deposit(account, amount):
            print("\n存款成功！")
        else:
            print("\n存款失败：账号不存在。")

    def _handle_withdraw(self):
        print("\n[办理取款业务]")
        account = self._get_input("请输入您的账号: ")
        password = self._get_input("请输入您的6位密码: ", int, lambda p: len(str(p)) == 6)
        amount = self._get_input("请输入取款金额: ", int, lambda a: a > 0, "取款金额必须大于0！")

        status = self.bank.withdraw(account, password, amount)

        if status == 0:
            print("\n取款成功！")
        elif status == 1:
            print("\n取款失败：账号不存在。")
        elif status == 2:
            print("\n取款失败：密码错误。")
        elif status == 3:
            print("\n取款失败：余额不足。")

    def _handle_transfer(self):
        print("\n[办理转账业务]")
        from_account = self._get_input("请输入您的(转出)账号: ")
        password = self._get_input("请输入您的(转出)账号密码: ", int, lambda p: len(str(p)) == 6)
        to_account = self._get_input("请输入对方的(转入)账号: ")
        amount = self._get_input("请输入转账金额: ", int, lambda a: a > 0, "转账金额必须大于0！")

        status = self.bank.transfer(from_account, to_account, password, amount)

        if status == 0:
            print("\n转账成功！")
        elif status == 1:
            print("\n转账失败：转出或转入账号不存在。")
        elif status == 2:
            print("\n转账失败：您的密码错误。")
        elif status == 3:
            print("\n转账失败：您的余额不足。")

    def _handle_query(self):
        print("\n[办理查询业务]")
        account = self._get_input("请输入您要查询的账号: ")
        password = self._get_input("请输入密码: ", int, lambda p: len(str(p)) == 6)

        self.bank.query_account(account, password)  # 该方法自己会打印信息

    def run(self):
        """启动ATM机，进入主循环"""
        while True:
            self._display_menu()
            choice = input("请输入您的选择(1-6): ")

            if choice == '1':
                self._handle_open_account()
            elif choice == '2':
                self._handle_deposit()
            elif choice == '3':
                self._handle_withdraw()
            elif choice == '4':
                self._handle_transfer()
            elif choice == '5':
                self._handle_query()
            elif choice == '6':
                print("\n感谢您的使用，再见！")
                break
            else:
                print("\n无效的选择，请输入1到6之间的数字。")

            input("\n按任意键返回主菜单...")


# ==============================================================================
# 5. 主程序入口
# ==============================================================================
if __name__ == "__main__":
    # 1. 创建一个银行实例
    agri_bank = Bank(name="中国农业银行昌平沙河支行")

    # 2. 创建一个ATM实例，并传入银行对象
    atm_machine = ATM(bank=agri_bank)

    # 3. 启动ATM程序
    atm_machine.run()